﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

namespace EndlessGalaxy
{
    public class Skybox
    {
        public string name;
        public List<Planet> planets;
        public List<Ship> ships;
        public Image background;
        public Image displayed;
        public int mplanets;
        public int suns;

        public Skybox()
        {
            planets = new List<Planet>();
            ships = new List<Ship>();
            Bitmap pic = new Bitmap(600, 600);
            background = Image.FromHbitmap(pic.GetHbitmap());
            Graphics canvas = Graphics.FromImage(background);
            canvas.FillRectangle(Brushes.Black, 0, 0, background.Width, background.Height);
            int numstars = Globals.randint.Next(100, 401);
            for (int x = 0; x < numstars; x++)
            {
                Point loc = new Point(Globals.randint.Next(0, 599), Globals.randint.Next(0, 599));
                int sz = Globals.randint.Next(1, Math.Min(600 - Math.Max(loc.X, loc.Y), 4));
                switch (Globals.randint.Next(1, 11))
                {
                    case 1:
                        canvas.FillEllipse(Brushes.White, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 2:
                        canvas.FillEllipse(Brushes.AliceBlue, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 3:
                        canvas.FillEllipse(Brushes.Azure, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 4:
                        canvas.FillEllipse(Brushes.LightYellow, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 5:
                        canvas.FillEllipse(Brushes.BlanchedAlmond, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 6:
                        canvas.FillEllipse(Brushes.MintCream, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 7:
                        canvas.FillEllipse(Brushes.LightPink, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 8:
                        canvas.FillEllipse(Brushes.Aqua, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 9:
                        canvas.FillEllipse(Brushes.Ivory, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    case 10:
                        canvas.FillEllipse(Brushes.Snow, new Rectangle(loc, new Size(sz, sz)));
                        break;
                    default:
                        canvas.FillEllipse(Brushes.GhostWhite, new Rectangle(loc, new Size(sz, sz)));
                        break;
                }
            }
            displayed = background.Clone() as Image;
            GeneratePlanets();
            Drawplanets();
            name = Globals.GetRandomName(displayed);
            AddNames();
        }

        public void GeneratePlanets()
        {
            int stars = Globals.randint.Next(0, 9);
            switch (stars)
            {
                case 6:
                case 7:
                    stars = 2;
                    break;
                case 8:
                    stars = 3;
                    break;
                default:
                    stars = 1;
                    break;
            }
            planets = new List<Planet>();
            for (int x = 0; x < stars; x++)
            {
                int startype= Globals.randint.Next(1,1001);
                if (startype >= 995)
                {
                    if (startype == 1000)
                    {
                        planets.Add(new Planet(Planet.worldType.bHole));
                    }
                    else
                    {
                        planets.Add(new Planet(Planet.worldType.neutronstar));
                    }
                }
                else
                {
                    planets.Add(new Planet(Planet.worldType.star));
                }
                planets[x].imgsize = Math.Max(Convert.ToInt32(Math.Max(Math.Log(planets[x].radius, 5.0), 1.0)), 3);
            }
            double angle = 0;
            for (int x = 0; x < stars; x++)
            {
                planets[x].location = StellarOrbit(stars, x, angle, out angle);
            }
            int numplanets = 0;
            double maxplanets = 0.0;
            foreach (Planet sun in planets)
            {
                maxplanets += (sun.gravity / (Globals.solarmass / 10));
            }
            if (planets.Count > 1)
            {
                if (planets.Count > 2)
                {
                    maxplanets /= 50;
                }
                else
                {
                    maxplanets /= 20;
                }
            }
            numplanets = Math.Min(Globals.randint.Next(0, Convert.ToInt32(maxplanets)), 20);
            mplanets = numplanets;
            suns = planets.Count;
            for (int x = suns; x < numplanets + suns; x++)
            {
                int planettype = Globals.randint.Next(1, 1001);
                if (planettype >= 650)
                {
                    if (planettype >= 900)
                    {
                        planets.Add(new Planet(Planet.worldType.asteroidbelt));
                    }
                    else
                    {
                        planets.Add(new Planet(Planet.worldType.rockyWorld));
                    }
                }
                else
                {
                    planets.Add(new Planet(Planet.worldType.gasGiant));
                }
            }
            for (int x = suns; x < numplanets + suns; x++)
            {
                planets[x].imgsize = Convert.ToInt32(Math.Max(Math.Log(planets[x].radius, 5.0), 2.0));
                int r = 80 + ((x-suns) * (200 / (mplanets)));
                planets[x].location = PlanetOrbit(r);
                //if (planets[x].type == Planet.worldType.asteroidbelt)
                //{
                //    planets[x].location = new Point(r, 0);
                //}
            }
            int comets = mplanets / 10;
            for (int x = suns + mplanets; x < suns + mplanets + comets; x++)
            {
                planets.Add(new Planet(Planet.worldType.comet));
                planets[x].imgsize = Convert.ToInt32(Math.Max(Math.Log(planets[x].radius, 5.0), 2.0));
                planets[x].location = new Point(Globals.randint.Next(10, 590), Globals.randint.Next(10, 590));
            }
            for (int x = suns; x < suns + mplanets; x++)
            {
                int tmpplanets = planets.Count;
                int moons = Globals.randint.Next(0, Convert.ToInt32(Math.Log(planets[x].Mass(),1000000000000.0)));
                for (int y = tmpplanets; y <= tmpplanets + moons; y++)
                {
                    switch (planets[x].type)
                    {
                        case Planet.worldType.asteroidbelt:
                            planets.Add(new Planet(Planet.worldType.dwarfplanet));
                            break;
                        default:
                            planets.Add(new Planet(Planet.worldType.moon));
                            break;
                    }
                    planets[y].homeindex = x;
                    planets[y].imgsize = Convert.ToInt32(Math.Max(Math.Log(planets[y].radius, 5.0), 2.0));
                    planets[y].location = MoonOrbit(planets[x].imgsize*2 + (8 * (tmpplanets - y)), planets[x].location);
                }
            }
            for (int x = suns; x < planets.Count; x++)
            {
                planets[x].radiation = 0.0;
                for (int y = 0; y < suns; y++)
                {
                    planets[x].radiation += (planets[y].radiation /
                        (Math.Pow(Globals.Distance(planets[x].location, planets[y].location), 2.0)));
                }
                foreach (Cargo stuff in planets[x].atmosphere)
                {
                    planets[x].radiation /= Math.Pow((stuff.amount * stuff.density), 0.01);
                }
                planets[x].aveTemp = planets[x].radiation;
                foreach (Cargo stuff in planets[x].atmosphere)
                {
                    if (Globals.materials[stuff.name].attributes.Contains("GREENHOUSE"))
                    {
                        planets[x].aveTemp *= Math.Pow(stuff.amount, 0.01);
                    }
                }
            }
            Drawplanets();
        }

        public override string ToString()
        {
            return name + ": Planets: " + Convert.ToString(planets.Count);
        }

        public void Drawplanets()
        {
            displayed = background.Clone() as Image;
            Graphics canvas = Graphics.FromImage(displayed);
            foreach (Planet here in planets)
            {
                here.imgsize = Math.Max(here.imgsize, 5);
                SolidBrush brush = new SolidBrush(here.color);
                canvas.FillEllipse(brush, here.location.X - (here.imgsize / 2),
                    here.location.Y - here.imgsize / 2, here.imgsize, here.imgsize);
                if (here.hasColony)
                {
                    canvas.FillEllipse(Brushes.Red, here.location.X - ((here.imgsize * 3) / 8),
                        here.location.Y - ((here.imgsize * 3) / 8), (here.imgsize * 3) / 4, (here.imgsize * 3) / 4);
                }
            }
        }

        public void DrawShips()
        {
            Graphics canvas = Graphics.FromImage(displayed);
            SolidBrush brush = new SolidBrush(Color.Purple);
            foreach (Ship that in ships)
            {
                if (that.orbitedindex == -1)
                {
                    canvas.FillEllipse(brush, that.location.X - (3),
                        that.location.Y - (3), 6, 6);
                }
                else
                {
                    canvas.FillEllipse(brush, planets[that.orbitedindex].location.X - (planets[that.orbitedindex].imgsize / 2),
                        planets[that.orbitedindex].location.Y - (planets[that.orbitedindex].imgsize / 4),
                        planets[that.orbitedindex].imgsize, planets[that.orbitedindex].imgsize / 2);
                }
            }
        }

        public Point StellarOrbit(int numstars, int starnum, double oldangle, out double Angle)
        {
            double angle = oldangle;
            int radius = 0;
            Point final = new Point(0,0);
            foreach (Planet here in planets)
            {
                radius += here.imgsize;
            }
            switch (numstars)
            {
                case 2:
                    if (starnum == 0)
                    {
                        angle = 2.0 * Math.PI * Globals.randint.NextDouble();
                        final.X = Convert.ToInt32(300 + radius * Math.Cos(angle));
                        final.Y = Convert.ToInt32(300 + radius * Math.Sin(angle));
                        Angle = angle;
                        return final;
                    }
                    else
                    {
                        final.X = Math.Abs(planets[0].location.X - 600);
                        final.Y = Math.Abs(planets[0].location.Y - 600);
                        Angle = 0;
                        return final;
                    }
                case 3:
                    if (starnum == 0)
                    {
                        angle = 2.0 * Math.PI * Globals.randint.NextDouble();
                        final.X = Convert.ToInt32(300 + radius * Math.Cos(angle));
                        final.Y = Convert.ToInt32(300 + radius * Math.Sin(angle));
                        Angle = angle;
                        return final;
                    }
                    else
                    {
                        angle = oldangle + ((2d / 3d) * Math.PI);
                        final.X = Convert.ToInt32(300 + radius * Math.Cos(angle));
                        final.Y = Convert.ToInt32(300 + radius * Math.Sin(angle));
                        Angle = angle;
                        return final;
                    }
                default:
                    Angle = 0;
                    return new Point(Globals.CenterRectangle(planets[starnum].imgsize, Globals.MainWindow.MainView.Width),
                        Globals.CenterRectangle(planets[starnum].imgsize, Globals.MainWindow.MainView.Height));
            }
        }

        public Point PlanetOrbit(int radius)
        {
            double angle = 2.0 * Math.PI * Globals.randint.NextDouble();
            return new Point(Convert.ToInt32(300 + (radius * Math.Cos(angle))), Convert.ToInt32(300 + (radius * Math.Sin(angle))));
        }

        public Point MoonOrbit(int radius, Point center)
        {
            double angle = 2.0 * Math.PI * Globals.randint.NextDouble();
            return new Point(Convert.ToInt32(center.X + (radius * Math.Cos(angle))), Convert.ToInt32(center.Y + (radius * Math.Sin(angle))));
        }

        public bool HasHabitable()
        {
            foreach (Planet here in planets)
            {
                if (here.type == Planet.worldType.rockyWorld)
                {
                    return true;
                }
            }
            return false;
        }

        public bool HasHabitable(List<string> atts)
        {
            foreach (Planet here in planets)
            {
                if ((here.type == Planet.worldType.rockyWorld) || (here.type == Planet.worldType.gasGiant))
                {
                    bool yep = false;
                    foreach (string att in atts)
                    {
                        yep = false;
                        foreach (Cargo stuff in here.composition)
                        {
                            if (Globals.materials[stuff.name].attributes.Contains(att))
                            {
                                yep = true;
                                break;
                            }
                            List<string> mats = new List<string>();
                            foreach (string sym in Globals.materials[stuff.name].contents().Keys)
                            {
                                mats.Add(sym);
                            }
                            foreach (string mat in mats)
                            {
                                if (Globals.materials[mat].attributes.Contains(att))
                                {
                                    yep = true;
                                    break;
                                }
                            }
                        }
                        if (!yep)
                        {
                            break;
                        }
                    }
                    if (yep)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
        public int HabitableWorld(List<string> atts)
        {
            for (int i = 0; i < planets.Count; i++)
            {
                Planet here = planets[i];
                if ((here.type == Planet.worldType.rockyWorld) || (here.type == Planet.worldType.gasGiant))
                {
                    bool yep = false;
                    foreach (string att in atts)
                    {
                        yep = false;
                        foreach (Cargo stuff in here.composition)
                        {
                            if (Globals.materials[stuff.name].attributes.Contains(att))
                            {
                                yep = true;
                                break;
                            }
                            List<string> mats = new List<string>();
                            foreach (string sym in Globals.materials[stuff.name].contents().Keys)
                            {
                                mats.Add(sym);
                            }
                            foreach (string mat in mats)
                            {
                                if (Globals.materials[mat].attributes.Contains(att))
                                {
                                    yep = true;
                                    break;
                                }
                            }
                        }
                        if (!yep)
                        {
                            break;
                        }
                    }
                    if (yep)
                    {
                        return i;
                    }
                }
            }
            return -1;
        }

        public bool HasBHole()
        {
            if (planets.Count > 0)
            {
                foreach (Planet here in planets)
                {
                    if (here.type == Planet.worldType.bHole)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        public void Cleanup()
        {
            for (int i = planets.Count - 1; i >= 0; i--)
            {
                foreach (Planet here in planets)
                {
                    for (int j = here.composition.Count - 1; j >= 0; j--)
                    {
                        if ((here.composition[j].name == "NULLREF") || (here.composition[j].name == "NullRef") || 
                            (here.composition[j].amount <= 0))
                        {
                            here.composition.RemoveAt(j);
                        }
                    }
                    for (int j = here.surface.Count - 1; j >= 0; j--)
                    {
                        if ((here.surface[j].name == "NULLREF") || (here.surface[j].name == "NullRef") || (here.surface[j].amount <= 0))
                        {
                            here.surface.RemoveAt(j);
                        }
                    }
                    for (int j = here.atmosphere.Count - 1; j >= 0; j--)
                    {
                        if ((here.atmosphere[j].name == "NULLREF") || (here.atmosphere[j].name == "NullRef") || 
                            (here.atmosphere[j].amount <= 0))
                        {
                            here.atmosphere.RemoveAt(j);
                        }
                    }
                }
                foreach (Ship here in ships)
                {
                    for (int j = here.cargo.Count - 1; j >= 0; j--)
                    {
                        if ((here.cargo[j].name == "NULLREF") || (here.cargo[j].name == "NullRef") || (here.cargo[j].amount <= 0))
                        {
                            here.cargo.RemoveAt(j);
                        }
                    }
                }
                while ((Math.Pow(planets[i].Gravity(), 1.0) < Cargo.Sum(planets[i].surface)) && (Cargo.Sum(planets[i].surface) > 0))
                {
                    Cargo.GiveCargo(Cargo.TakeCargo(planets[i].surface[Globals.randint.Next(0, planets[i].surface.Count)].name,
                        1.0, planets[i].surface), planets[i].composition);
                }
                planets[i].Reevaluate(suns);
                if ((planets[i].type == Planet.worldType.NullRef) || (planets[i].Gravity() <= 0))
                {
                    planets.RemoveAt(i);
                }
            }
        }

        public void AddNames()
        {
            string currentbelt = "";
            string currentcomet = "";
            string currentplanet = "";
            char currentsub = '-';
            for (int i = 0; i < planets.Count; i++)
            {
                switch (planets[i].type)
                {
                    case Planet.worldType.bHole:
                    case Planet.worldType.neutronstar:
                    case Planet.worldType.star:
                        currentsub = Globals.IncrementLetter(currentsub);
                        planets[i].name = name + " " + currentsub;
                        planets[i].shortname = Convert.ToString(currentsub);
                        break;
                    case Planet.worldType.gasGiant:
                    case Planet.worldType.rockyWorld:
                        currentplanet = Globals.IncrementRomans(currentplanet);
                        planets[i].name = name + " " + currentplanet;
                        planets[i].shortname = currentplanet;
                        currentsub = '-';
                        break;
                    case Planet.worldType.moon:
                    case Planet.worldType.asteroidmoon:
                        if (planets[planets[i].homeindex].shortname != currentplanet)
                        {
                            currentsub = '-';
                            currentplanet = planets[planets[i].homeindex].shortname;
                        }
                        currentsub = Globals.IncrementLetter(currentsub);
                        planets[i].shortname = planets[planets[i].homeindex].shortname + currentsub;
                        planets[i].name = name + " " + planets[i].shortname;
                        break;
                    case Planet.worldType.asteroidbelt:
                        currentbelt = Globals.IncrementRomans(currentbelt);
                        currentsub = '-';
                        planets[i].shortname = "Belt " + currentbelt;
                        planets[i].name = name + " " + planets[i].shortname;
                        break;
                    case Planet.worldType.dwarfplanet:
                        if (planets[planets[i].homeindex].shortname != currentplanet)
                        {
                            currentsub = '-';
                            currentplanet = planets[planets[i].homeindex].shortname;
                        }
                        currentsub = Globals.IncrementLetter(currentsub);
                        planets[i].shortname = planets[planets[i].homeindex].shortname + currentsub;
                        planets[i].name = name + " " + planets[i].shortname;
                        break;
                    case Planet.worldType.comet:
                        currentcomet = Globals.IncrementRomans(currentcomet);
                        planets[i].shortname = "Comet " + currentcomet;
                        planets[i].name = name + " " + planets[i].shortname;
                        break;
                    default:
                        planets[i].name = name + " system unnamed object";
                        planets[i].shortname = "Unnamed object";
                        break;
                }
            }
        }

        public List<Ship> Orbits(int i)
        {
            List<Ship> orbits = new List<Ship>();
            foreach (Ship that in ships)
            {
                if (that.orbitedindex == i)
                {
                    orbits.Add(that);
                }
            }
            return orbits;
        }

    }
}
